#include "stdafx.h"

#include "parser_internal.h"
#include "parser_state.h"
#include "parser_errors.h"

#include "ast_statements.h"
#include "ast_expressions.h"

namespace funpl
{

namespace parser
{

/// Parses a variable.
ast::variable* parse_variable(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	auto variable = state.tree->create_node<ast::variable>(parent);

	// Copy
	if (accept_cont(state, token_type::op_add))
		variable->variableClass = ast::variable_class::copy;
	// Output
	else if (accept_cont(state, token_type::op_and))
		variable->variableClass = ast::variable_class::output;
	// Destructible
	else if (accept_cont(state, token_type::op_tilde))
		variable->variableClass = ast::variable_class::destructible;

	// Variable name
	variable->name = expect_cont(state, token_type::tok_identifier).token.range;

	// Variable type
	if (accept_cont(state, token_type::tok_op_colon))
		variable->type = parse_type(state, variable, implicitSemicolon);

	// Initialization
	if (accept_cont(state, token_type::op_assign))
		variable->init = parse_expression(state, variable, implicitSemicolon);
	// Constructor notation
	else if (check(state, token_type::tok_oparen, implicitSemicolon))
	{
		// TODO: better alternatives than type expression?
		ast::type_expression *typeExpr = state.tree->create_node<ast::type_expression>(variable);
		typeExpr->type = variable->type;
		variable->init = parse_function_evaluation(state, parent, typeExpr);
	}

	return variable;
}

/// Parses a variable definition.
ast::variable* parse_var(parser_state &state, ast::node *parent)
{
	expect(state, token_type::kw_var);

	return parse_variable(state, parent, true);
}

/// Parses a constant variable definition.
ast::variable* parse_def(parser_state &state, ast::node *parent)
{
	expect(state, token_type::kw_def);

	return parse_variable(state, parent, true);
}

/// Parses a while statement.
ast::while_statement* parse_while_statement(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect(state, token_type::kw_while);

	auto while_statement = state.tree->create_node<ast::while_statement>(parent);

	// Condition
	expect_cont(state, token_type::tok_oparen);

	while_statement->condition = parse_expression(state, while_statement, false);

	expect_cont(state, token_type::tok_cparen);

	// Loop
	if (!accept_cont(state, token_type::tok_semicolon))
		while_statement->loop = parse_expression(state, while_statement, implicitSemicolon);
	else
		// Allow for nop loops
		while_statement->loop = state.tree->create_node<ast::compound>(while_statement);

	return while_statement;
}

/// Parses a do-while statement.
ast::do_while_statement* parse_do_while_statement(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect(state, token_type::kw_do);

	auto do_while_statement = state.tree->create_node<ast::do_while_statement>(parent);

	// Loop
	do_while_statement->loop = parse_expression(next_token_cont(state), do_while_statement, implicitSemicolon);

	// Accept one optional trailing semicolon
	accept_cont(state, token_type::tok_semicolon);

	expect_cont(state, token_type::kw_while);

	// Condition
	expect_cont(state, token_type::tok_oparen);

	do_while_statement->condition = parse_expression(state, do_while_statement, false);

	expect_cont(state, token_type::tok_cparen);

	return do_while_statement;
}

/// Parses a for statement.
ast::for_statement* parse_for_statement(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect(state, token_type::kw_for);

	auto for_statement = state.tree->create_node<ast::for_statement>(parent);

	expect_cont(state, token_type::tok_oparen);

	// Allow for empty definitions
	if (!check_cont(state, token_type::tok_semicolon))
		// Definitions
		do
		{
			auto variable = parse_variable(state, for_statement, false);
			for_statement->definitions.push_back( ast::for_statement::definition(variable->name, variable) );

			// Detect range-based for
			if (accept_cont(state, token_type::kw_in))
			{
				// TODO: disallow variable definition to contain initialization?

				auto range = parse_expression(state, for_statement, false);
				for_statement->ranges.push_back( ast::for_statement::range(variable->name, range) );
			}
		}
		while (accept_cont(state, token_type::tok_comma));

	// Condition & step expressions optional in range-based for statement
	if (for_statement->ranges.empty() || check_cont(state, token_type::tok_semicolon))
	{
		expect_cont(state, token_type::tok_semicolon);

		// Allow for empty condition
		if (!check_cont(state, token_type::tok_semicolon))
			// Condition
			for_statement->condition = parse_expression(state, for_statement, false);

		expect_cont(state, token_type::tok_semicolon);

		// Allow for empty step
		if (!check_cont(state, token_type::tok_cparen))
			// Step
			for_statement->step = parse_expression(state, for_statement, false);
	}

	expect_cont(state, token_type::tok_cparen);

	// Loop
	if (!accept_cont(state, token_type::tok_semicolon))
		for_statement->loop = parse_expression(state, for_statement, implicitSemicolon);
	else
		// Allow for nop loops
		for_statement->loop = state.tree->create_node<ast::compound>(for_statement);

	return for_statement;
}

/// Parses an if statement.
ast::if_statement* parse_if_statement(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect(state, token_type::kw_if);

	auto if_statement = state.tree->create_node<ast::if_statement>(parent);

	// Condition
	expect_cont(state, token_type::tok_oparen);

	if_statement->condition = parse_expression(state, if_statement, false);

	expect_cont(state, token_type::tok_cparen);

	// Consequence
	if (!accept_cont(state, token_type::tok_semicolon))
	{
		if_statement->consequence = parse_expression(state, if_statement, implicitSemicolon);

		// Accept one optional trailing semicolon
		accept_cont(state, token_type::tok_semicolon);
	}
	else
		// Allow for nop
		if_statement->consequence = state.tree->create_node<ast::compound>(if_statement);

	// Alternative
	if (accept_cont(state, token_type::kw_else))
		if_statement->alternative = parse_expression(next_token_cont(state), if_statement, implicitSemicolon);

	return if_statement;
}

/// Parses a catch statement.
ast::catch_statement* parse_catch_statement(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect_cont(state, token_type::kw_catch);

	auto catch_statement = state.tree->create_node<ast::catch_statement>(parent);

	// Exception type
	expect_cont(state, token_type::tok_oparen);

	catch_statement->exception = parse_variable(state, catch_statement, false);

	expect_cont(state, token_type::tok_cparen);

	// Catch
	catch_statement->handling = parse_expression(next_token_cont(state), catch_statement, implicitSemicolon);

	// Next catch in chain
	if (check_cont(state, token_type::kw_catch))
		catch_statement->next = parse_catch_statement(state, catch_statement, implicitSemicolon);

	return catch_statement;
}

/// Parses a try-catch statement.
ast::try_statement* parse_try_statement(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect_cont(state, token_type::kw_try);

	auto try_statement = state.tree->create_node<ast::try_statement>(parent);

	// Try
	try_statement->statements = parse_expression(next_token_cont(state), try_statement, implicitSemicolon);

	// Catch
	try_statement->handlers = parse_catch_statement(state, try_statement, implicitSemicolon);

	return try_statement;
}

/// Parses a throw statement.
ast::throw_statement* parse_throw_statement(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect(state, token_type::kw_throw);

	auto throw_statement = state.tree->create_node<ast::throw_statement>(parent);

	// Exception
	throw_statement->exception = parse_expression(state, throw_statement, implicitSemicolon);

	return throw_statement;
}

/// Parses a return statement.
ast::return_statement* parse_return_statement(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect(state, token_type::kw_return);

	auto return_statement = state.tree->create_node<ast::return_statement>(parent);

	// Value
	return_statement->value = parse_expression(state, return_statement, implicitSemicolon);

	return return_statement;
}

/// Parses a new expression.
ast::new_expression* parse_new_expression(parser_state &state, ast::node *parent)
{
	expect_cont(state, token_type::kw_new);

	auto new_ = state.tree->create_node<ast::new_expression>(parent);

	// Type / constructor
	new_->constructor = parse_type(state, new_, false);

	// Allow for argumentless notation
	if (check_cont(state, token_type::tok_oparen))
		new_->constructorArgs = parse_function_arguments(state, new_);

	return new_;
}

/// Parses a delete expression.
ast::delete_expression* parse_delete_expression(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect_cont(state, token_type::kw_delete);

	auto delete_ = state.tree->create_node<ast::delete_expression>(parent);

	// Expression
	delete_->operand = parse_expression(state, delete_, implicitSemicolon);

	return delete_;
}

/// Parses a guard expression.
ast::guard_expression* parse_guard_expression(parser_state &state, ast::node *parent, bool implicitSemicolon)
{
	expect_cont(state, token_type::kw_guard);

	auto guard = state.tree->create_node<ast::guard_expression>(parent);

	// Guarded variable
	expect_cont(state, token_type::tok_oparen);
	guard->name = expect_cont(state, token_type::tok_identifier).token.range;
	expect_cont(state, token_type::tok_cparen);

	// Guard expression
	guard->guard = parse_expression(state, guard, implicitSemicolon);

	return guard;
}

/// Parses an unguard expression.
ast::unguard_expression* parse_unguard_expression(parser_state &state, ast::node *parent)
{
	expect_cont(state, token_type::kw_unguard);

	auto unguard = state.tree->create_node<ast::unguard_expression>(parent);

	// Guarded variable
	expect_cont(state, token_type::tok_oparen);
	unguard->name = expect_cont(state, token_type::tok_identifier).token.range;
	expect_cont(state, token_type::tok_cparen);

	return unguard;
}

/// Parses a statement.
ast::statement* parse_statement(parser_state &state, ast::node *parent)
{
	ast::statement *statement = nullptr;

	switch (next_token(state).token.type)
	{
	case token_type::kw_for:
		return parse_for_statement(state, parent, true);
	case token_type::kw_while:
		return parse_while_statement(state, parent, true);
	case token_type::kw_do:
		return parse_do_while_statement(state, parent, true);

	case token_type::kw_def:
		statement = parse_def(state, parent);
		break;
	case token_type::kw_var:
		statement = parse_var(state, parent);
		break;

	case token_type::kw_throw:
		statement = parse_throw_statement(state, parent, true);
		break;
	case token_type::kw_return:
		statement = parse_return_statement(state, parent, true);
		break;

	default:
		statement = parse_expression(state, parent, true);
	}

	return LEAN_ASSERT_NOT_NULL(statement);
}

/// Parses a compound.
ast::compound* parse_compound(parser_state &state, ast::node *parent)
{
	expect_cont(state, token_type::tok_obrace);

	auto compound = state.tree->create_node<ast::compound>(parent);

	// Block body
	while (!accept_is(state, token_type::tok_cbrace))
	{
		try
		{
			// Statement
			auto statement = parse_statement(state, compound);
			compound->statements.push_back(statement);

			// Make sure that the full statement has been parsed
			if (!check(state, token_type::tok_cbrace))
				expect(state, token_type::tok_semicolon);
		}
		catch(const parse_exception&) { skip_error(state); } // TODO: skip till semicolon instead?
	}

	return compound;
}

} // namespace

} // namespace